<?php

class locale_phones extends singleton
{
	// php singleton wrapper
	static public function &__getMe( ) { return parent::__getMe( __CLASS__ ); }
	static public function &instance() { return parent::__getMe( __CLASS__ ); }
	
	protected $options = array( // new york, manhattan location
		'long_prefix'		=> '00',
		'international_prefix' => '011',
		'country_code'		=> '+1',
		'area_code'			=> "212",
		'customer_length'	=> "7",
		);
	protected $tinylist = null;
	protected $fulllist = null;
	
	protected function __construct()
	{
		$this ->initialize();
	}
	
	protected function initialize()
	{
		$config = config::instance();
		foreach( (array)($config ->locale__phone) as $k => $v )
			$this ->options[ $k ] = $v;

		$this ->options['country_code'] = '+'.trim( $this ->options['country_code'], '+' );
		
		$dbfile = dbfile::instance();
		if( $dbfile ->isExists( 'phone_prefixes_tiny' ) )
			$this ->tinylist = $dbfile ->getBy( 'phone_prefixes_tiny' );
		if( $dbfile ->isExists( 'phone_prefixes' ) )
			$this ->fulllist = $dbfile ->getBy( 'phone_prefixes' );
	}
	
	protected function getFormatByPrefix( $prefix )
	{
		return $this ->tinylist[ $prefix ];
	}
	
	protected function getFormat( $phone )
	{
		$prefix = $this ->findPrefix( $phone );
		$format = $xformat = $this ->getFormatByPrefix( $prefix );
		
		if( strpos( $xformat, '..' ) !== false || strpos( $xformat, '*' ) !== false )
		{
			$phone = $this ->cleanup( $phone, 1 );
			$format_length = strlen( $phone ) - $this ->getLength( $xformat, 0, true );
			if( $format_length < 0 )
				return '';
			
			$format_part = $this ->renderFormatForDigits( $format_length );
			$format = (strpos( $xformat, '..' ) !== false)
				? preg_replace( '~[0-9]\.\.[0-9]~', $format_part, $xformat )
				: str_replace( '*', $format_part, $xformat );
		}
		
		return $format;
	}

	protected function findPrefix( $phone )
	{
		$phone = $this ->cleanup( $phone, 1 );
		$l = strlen( $phone );
		//var_dump( compact( 'phone', 'l' ) );
		if( $l < 2 ) return null;
		
		$prefixes = $this ->tinylist;
		
		for( $i = min(9,$l); $i > 0; $i-- )
		{
			$prefix = substr( $phone, 0, $i );
			if( isset( $prefixes[ $prefix ] ) && $prefix != $phone )
				return $prefix;
		}
		
		return null;
	}
	
	public function validate( $phone )
	{
		$phone = $this ->cleanup( $phone );
		
		//var_dump( array( 'valid_phone' => $phone ) );
		$prefix = $this ->findPrefix( $phone );
		if( !$prefix ) return false;
		
		//if( strpos( $phone, '83122' ) === 0 ) var_dump( compact( 'phone', 'prefix' ) );
		$format = $this ->getFormatByPrefix( $prefix );
		$length = $this ->getLength( $format );
		$phonelength = strlen( ltrim($phone,'+') );
		
		if( !is_array( $length ) )
			return $phonelength == $length;
		
		if( $phonelength < $length[0] )
			return false;
		
		if( isset( $length[1] ) && $phonelength > $length[1] )
			return false;
		
		return true;
	}
	
	public function format( $phone, $format = null )
	{
		$phone = $this ->cleanup( $phone );
		$l = strlen( $phone );
		if( $l < 2 ) return null;
		
		if( is_null( $format ) )
			$format = $this ->getFormat( $phone );
		
		$tok = ' -';
		$phone = $this ->cleanup( $phone, 1 );
		
		$result = '';
		$partlength = strtok( $format, $tok );
		$position = 0;
		//var_dump( array( 'phone' => $phone, 'format' => $format, 'partlength' =>$partlength ) );
		do {
			$position += strlen( $partlength );
			$part = substr( $phone, 0, $partlength );
			$delimiter = substr( $format, $position, 1 );
			$position ++;
			$phone = substr( $phone, $partlength );
			
			if( strpos( $format, '..' ) !== false )
				var_dump( 'If you see this please send bug report to developers — It can\'t be!', compact( 'phone', 'format', 'position', 'part', 'delimiter' ) );
			
			$result .= $part.$delimiter;
			
			$partlength = strtok( $tok );
		} while( $partlength !== false );
		
		if( !strlen( $result ) )
			return $phone;
		
		return '+'.$result;
	}
	
	protected function getLength( $format, $offset = 0, $staticonly = false )
	{
		$tok = ' -';
		
		$length = 0;
		$part = strtok( $format, $tok );
		
		do {
			if( $offset )
			{
				$less = min( $offset, $part );
				$more = max( $offset, $part ) - $part;
				
				$offset -= $less;
				$part -= $less;
				
				if( !$part )
					$part = strtok( $tok );
				
				continue;
			}
			
			if( $offset < 0 ) // debug
			{
				var_dump( '-', $less, $more, $part, $length, $offset );
				die;
			}
			
			if( strpos( $part, '..' ) !== false )
			{
				if( $staticonly ) break;
				list( $min, $max ) = explode( '..', $part );
				$min += $length;
				$max += $length;
				$length = array( $min, $max );
				break;
			}
			elseif( strpos( $part, '*' ) !== false )
			{
				if( $staticonly ) break;
				$length = array( $length, 16 );
				break;
			}
			else
			{
				$length += $part;
				$part = strtok( $tok );
			}
		} while( $part !== false );
		
		return $length;
	}

	public function renderFormatForDigits( $digits )
	{
		$digits = array_map( 'trim', explode( '-', str_replace( array( ',', 'to' ), '-', $digits ) ) );
		$min = reset( $digits );
		$max = end( $digits );
		
		if( $min < 5 && $max != $min )
		{
			$result = '2-'.($min-2).'..'.($max-2);
			return $result;
		}
		elseif( $min < 7 && $max != $min )
		{
			$result = '3-'.($min-3).'..'.($max-3);
			return $result;
		}

		if( $max == $min )
		{
			$result = ($min<5 ? ('2-'.($min-2))
				:($min<7 ? '2-'.($this ->renderFormatForDigits($min-2))
				:($min==7 ? '3-4'
				:($min==8 ? '3 2-3'
				:('3 '.($this ->renderFormatForDigits($min-3)))
				))));
			return $result;
		}
		
		$result = '3 ';
		$min -= 3;
		$max -= 3;
		
		$result .= ( $max-$min < 3 && $min > 5 )
			? phone_digits_to_format( $min.' to '.$max )
			: $min.'..'.$max;
		
		return $result;
	}
	
	
	public function cleanup( $phone, $justnumber = false )
	{
		$phone = str_replace( array( '#', ',', 'p' ), array( 'x', 'x', '~' ), $phone );
		$phone = preg_replace( '@[^0-9+~x]*@', '', $phone );
		
		list( $phone, $ext ) = explode( 'x', $phone.'x' );
		
		if( strlen( $phone ) < $this ->options['customer_length'] )
			return $phone;
		
		if( strlen( $phone ) == $this ->options['customer_length'] )
			$phone = $this ->options['country_code'].$this ->options['area_code'].$phone;
		
		if( strlen( $phone ) == strlen( $this ->options['area_code'] ) + $this ->options['customer_length'] )
			$phone = $this ->options['country_code'].$phone;
		
		if( $phone[0] != '+' && strpos( $phone, $this ->options['international_prefix'] ) === 0 )
			$phone = str_replace( $this ->options['international_prefix'], '+', $phone );
		elseif( $phone[0] != '+' && strpos( $phone, $this ->options['long_prefix'] ) === 0 )
			$phone = $this ->options['country_code'].substr( $phone, strlen($this ->options['long_prefix']) );
		
		if( strpos( $phone, '+70' ) === 0 )
			$phone[2] = 4;
		
		if( $justnumber )
			$phone = preg_replace( '@[^0-9~]@', '', $phone );

		//echo "\n~$phone~ ";
		
		if( strlen( $phone ) < 5 ) return false;
		return $phone;
	}
	
}